<?php
final class XAnnotationAOPRegister implements XClassRegister{
	private static $instance = null;

	private function __construct() {
	
	}
	
	/** 
	 * @return XAnnotationAOPRegister
	 */	
	static function singleton() {
		if (!self::$instance) {
			self::$instance = new self;
		}
		return self::$instance;
	}
		
	public function register($class){
		$annotation = XAnnotation::singleton($class);
		$annotations = $annotation->annotations();
		if(!isset($annotations['Aspect'])){
			return $this;
		}
		$methodAnnotations = $annotation->methodAnnotations();
		foreach($methodAnnotations as $method => $methodAnnotation){
			if(isset($methodAnnotation['Pointcut'])){
				$this->_registerPointcut($class, $method, $methodAnnotation['Pointcut']);
				continue;
			}
			foreach(array(
				'Before' => 'addBeforeAdvice',
				'AfterReturning' => 'addAfterReturningAdvice',
				'AfterThrowing' => 'addAfterThrowingAdvice',
				'After' => 'addAfterAdvice',
				'Around' => 'addAroundAdvice',
				) as $key => $value){
				if(isset($methodAnnotation[$key])){
					$this->_registerAdvice($class, $method, $methodAnnotation[$key], $value);
					break;
				}
			}
		}
		return $this;
	}
	
	private function _registerPointcut($class, $method, $pattern){	
		$pointcut = null;
		foreach(array(
			"execution" => 'XExecutionPointcut',
			"within" => 'XWithinPointcut',
			"target" => 'XTargetPointcut',
			"annotation" => 'XAnnotationPointcut',
			) as $key => $value){
			if(preg_match("/^{$key}\(.+\)/i", $pattern)){
				$pointcut = new $value($class, $method, $pattern);
				break;
			}
		}
		if(!is_null($pointcut)){
			XAOPContext::singleton()->addPointcut($pointcut);
		}
		return $pointcut;
	}
	
	private function _registerAdvice($class, $method, $params, $register){
		if(!is_array($params)){
			$pointcut = $this->_registerPointcut($class, $method, $params);
			if(!is_null($pointcut)){				
				$advice = new XBasicAdvice($class, $method, $pointcut->getName());
				XAOPContext::singleton()->$register($advice);
			}
			return $this;
		}
		if(!isset($params['pointcut'])){				
			return $this;
		}
		$name = $params['pointcut'];
		if(preg_match("/\w+\s*\|\s*\w+/i", $name)){
			$pointcuts = preg_split("/\s*\|\s*/i", $name);
			foreach($pointcuts as $pointcut){
				$this->_registerAdviceByName($class, $method, $pointcut, $register);
			}
			return $this;
		}
		$this->_registerAdviceByName($class, $method, $name, $register);
		return $this;
	}
	
	private function _registerAdviceByName($class, $method, $name, $register){
		if(!preg_match("/(\w+)\.(\w+)\(\)/i", $name, $match)
			&& preg_match("/(\w+)\(\)/i", $name, $match)){
				$name = $class.'.'.$name;
		}
		$advice = new XBasicAdvice($class, $method, $name);
		XAOPContext::singleton()->$register($advice);
	}
}
?>